#pragma once

#include "containers.hpp"

#include <glm/glm.hpp>

#define FILE_MAGIC { 0x3511D17F, 0x1176A4FB }

enum NodeProcessFlag : uint32_t
{
    FLAG_NONE = 0,
    FLAG_LEAF = 1 << 1,
    FLAG_VALIDVOXEL = 1 << 2,
    FLAG_CONNECTED = 1 << 3,
    FLAG_SELECTED = 1 << 4,
    FLAG_DELETING = 1 << 30,
};

struct Voxel
{
    glm::vec3 center;
    float size;
};

struct BuilderNode
{
    UseMemoryPool
    uint32_t mask = 0;
    uint32_t index = 0;
    Voxel voxel;
    BuilderNode* parent;
    BuilderNode* children[8];
    BuilderNode* proxy[6];
    uint32_t connectionCnt;
    BuilderNode** connections;
};

struct BuilderRoot
{
    float size;
    glm::vec3 center;
    uint32_t dimesions[3];
    uint32_t childrenCnt = 0;
    BuilderNode** children = nullptr;
};

struct BuilderNodeConnection
{
    BuilderNode* v0;
    BuilderNode* v1;
};

struct SpaceNode
{
    uint32_t mask = 0;
    Voxel voxel;
    uint32_t parent;
    uint32_t children[8];
    uint32_t connectionCnt;
    uint32_t* connections;
};

struct SpaceRoot
{
    uint32_t nodeCnt = 0;
    SpaceNode* nodes = nullptr;
    uint32_t connectionCnt = 0;
    uint32_t* connections = nullptr;

    float size;
    glm::vec3 center;
    uint32_t dimesions[3];
    uint32_t childrenCnt = 0;
    uint32_t* children = nullptr;
};

class ISpaceBuilderHandler
{
public:
    virtual bool ValidCond( const BuilderNode* vol ) = 0;  
    virtual void SplitCallback( const bool cond, BuilderNode* vol ) = 0;
    virtual void LeafCallback( const bool cond, BuilderNode* vol ) = 0;
};

template<typename FT>
void Foreach( BuilderNode* tree, const FT& func )
{
    if( tree == nullptr ) return;
    CircularDeque<BuilderNode*> queue(32);
    queue.PushBack( tree );
    while( queue.Size() != 0 )
    {
        BuilderNode* cur = queue[0];
        queue.PopFront();
        func( cur );
        for( uint32_t i = 0; i < 8; ++i )
        {
            BuilderNode* n = cur->children[i];
            if( n != nullptr )
            { queue.PushBack(n); }
        }
    }
}

void CreateBuilderRoot( BuilderRoot& root, glm::vec3 center, glm::vec3 volume, float minsize, ISpaceBuilderHandler* cond = nullptr );
void CreateBuilderTree( BuilderNode* tree, float minsize, uint32_t& id, ISpaceBuilderHandler* cond = nullptr );
void PruneBuilderRoot( BuilderRoot& root, bool(*pruneFunc)(const BuilderNode* tar) );
void PruneBuilderTree( BuilderNode* tree, bool(*pruneFunc)(const BuilderNode* tar) );
void ListBuilderRoot( BuilderRoot& root, BuilderNode** res, bool updateIndex = false );
void ListBuilderTree( BuilderNode* tree, BuilderNode** res, uint32_t& iter, bool updateIndex = false );
uint32_t FindBuilderRootCnt( BuilderRoot& root );
void FindBuilderNodeCnt( BuilderNode* tree, uint32_t& res );
void SetupBuilderProxies( BuilderRoot& root );
void SetupBuilderConnections( BuilderRoot& root );
void SelectBuilderArea( BuilderRoot& root, glm::vec3 selectPt );

void DeleteBuilderRoot( BuilderRoot& root );
void DeleteBuilderTree( BuilderNode* tar );

BuilderNode* SearchBuilderNode( glm::vec3 pos, BuilderNode* cur, bool(*leafFunc)(const BuilderNode* tar) );
BuilderNode* SearchBuilderRoot( glm::vec3 pos, BuilderRoot& root, bool(*leafFunc)(const BuilderNode* tar) );

void BuildSpaceRoot( BuilderRoot& root, SpaceRoot& res );

SpaceNode* SearchSpaceNode( glm::vec3 pos, SpaceNode* nList, uint32_t offset, bool(*leafFunc)( const SpaceNode* tar) );
SpaceNode* SearchSpaceRoot( glm::vec3 pos, SpaceRoot& root, bool(*leafFunc)( const SpaceNode* tar) );
void DeleteSpaceRoot( SpaceRoot& root );

void SerializeSpaceInfo( const SpaceRoot& root, char*& res, uint32_t& size );
void DeserializeSpaceInfo( SpaceRoot& root, const char* res, const uint32_t size );
int ExportToFile( const char* path, const SpaceRoot& root );
int ImportFromFile( const char* path, SpaceRoot& root );

enum AStarSearchState
{
    ASTAR_UNDEFINED,
    ASTAR_QUEUEED,
    ASTAR_PROCESSED,
};

struct AStarSearchNode
{
    AStarSearchState state = AStarSearchState::ASTAR_UNDEFINED;
    float heuristic = std::numeric_limits<float>::max();
    uint32_t previous = UINT32_MAX;
};

void FindPathInSpaceRoot( SpaceRoot& root, glm::vec3 s, glm::vec3 e, float sa, CircularDeque<glm::vec3>& wps,
    bool (*lfunc) ( const SpaceNode*), float (*hfunc) (SpaceNode*, glm::vec3) );

void BuildPathFromVoxelList( DynamicArray<uint32_t>& nStack, SpaceRoot& root, glm::vec3 s, glm::vec3 e, float sa, CircularDeque<glm::vec3>& wps );

void QuickSearch( SpaceRoot& root, uint32_t si, uint32_t ei, DynamicArray<uint32_t>& wps );

void AStarSearch( SpaceRoot& root, uint32_t si, uint32_t ei, glm::vec3 e, DynamicArray<uint32_t>& wps,
        bool (*lfunc) ( const SpaceNode*),  float (*hfunc) (SpaceNode*, glm::vec3) );
